#pragma once

#include <boost/optional.hpp>
#include <boostext/signal.hpp>
#include <boostext/util/make_shared.hpp>
#include <boostext/container/auto_list.hpp>

#include <engine.core/noncopyable.hpp>
#include <engine.core/quad.hpp>

#include "exports.hpp"
#include "NestedState.hpp"
#include "StyleDefinition.hpp"
#include "Layout.hpp"




#pragma warning (push)
#pragma warning (disable: 4251)




namespace Engine
{
	namespace Ui
	{
		/**
		 * A widget is the base class for any Ui element, visible on the screen.
		 * It implements the most basic behavior that any widget should have,
		 * while forcing the user to implement additional functions, necessary for
		 * the widget to behave in a useful manner.
		 */
		class ENGINE_UI_API Widget
			: public noncopyable
		{
		public:

			SHARED(Widget);

			typedef QVector<ptr>                                                         WidgetList;
			typedef QVectorIterator<ptr>                                                 WidgetIterator;

			// Simple notification signal with no arguments
			typedef boost::signals2::signal<void (Widget::weak_ptr)>                     notify;
			typedef boost::signals2::signal<void (Widget::weak_ptr, int2)>               notify_int2;
			typedef boost::signals2::signal<void (Widget::weak_ptr, intsize2)>           notify_intsize2;
			typedef boost::signals2::signal<void (Widget::weak_ptr, Widget::weak_ptr)>   notify_widget;

		private:

			//
			// Signals
			//

			notify          m_stateChanged;
			notify          m_mouseEnter;
			notify          m_mouseLeave;
			notify          m_mouseClicked;
			notify          m_mouseDblClicked;
			notify          m_moved;
			notify          m_resized;
			notify          m_destroyed;
			notify_widget   m_add;
			notify_widget   m_remove;

		protected:

			/**
			 * Create a widget. The widget has no size and is positioned
			 * in the upper left corner of the parent. It's background color
			 * is set to white.
			 */
			Widget(BasicStyleDefinition::ptr styleDefinition);

			/**
			 * Create a widget. Same as above, but there must be a parent present, because
			 * the parent's style is taken.
			 */
			Widget(Widget::weak_ptr parent);

			/**
			 * Post constructor that is called right after the actual constructor.
			 */
			virtual void post_create(Widget::ptr _this);

		public:

			MAKE_SHARED1(Widget, BasicStyleDefinition::ptr);
			MAKE_SHARED1(Widget, boost::weak_ptr<Widget>);

			/**
			 * Destroy this widget.
			 */
			virtual ~Widget();



			/**
			 * Get the current style's name of this widget.
			 */
			const WidgetStyleIdentifier& styleId() const;

			/**
			 * Create a StyleId for the entire hierarchy of this widget (including this one).
			 */
			void createStyleId(WidgetStyleIdentifier& styleId, StyleIdentifier widgetId = StyleIdentifier()) const;

			/**
			 * Get the current style definition.
			 *
			 * @see StyleDefinition for a detailed explanation about style definitions
			 */
			BasicStyleDefinition::ptr styleDefinition() const;

			/**
			 * Get the style of this widget.
			 *
			 * @see WidgetStyle for a detailed explanation about styles
			 */
			WidgetStyle::ptr style() const;

			/**
			 * Set the style definition for this widget.
			 * Also sets this style definition for every child.
			 *
			 * Calling this function invalidates both the widget & state style.
			 * It is necessary to manually request the widget style by calling
			 * setStyle() with the appropriate identifier.
			 */
			virtual void setStyleDefinition(BasicStyleDefinition::ptr styleDefinition);

			/**
			 * Sets the style for this particular widget.
			 * Will only affect children if a subclass decides to do so (ie,
			 * the style demands a child has another style).
			 * The widget will request the WidgetStyle for this specific identifier
			 * from the current style document (again).
			 */
			void setStyle(StyleIdentifier styleId);

			/**
			 * Updates the style for this widget.
			 */
			void updateStyle();




			/**
			 * Get the current layout of this widget.
			 */
			Layout::ptr layout() const;

			/**
			 * Set the layout for this widget.
			 */
			virtual const Layout::ptr& setLayout(const Layout::ptr& layout);




			/**
			 * @returns the parent of this widget
			 * @returns an empty weak_ptr in case this widget has no parent
			 */
			Widget::ptr parent() const;

			/**
			 * Adds the given widget to this widget as the topmost one.
			 */
			const Widget::ptr& add(const Widget::ptr& widget);

			/**
			 * Checks if the given widget is a child of this widget.
			 * Checks recursively.
			 */
			bool isChild(const Widget::ptr& widget) const;

			/**
			 * Checks if the given widget is a child of this widget only.
			 */
			bool isDirectChild(const Widget::ptr& widget) const;

			/**
			 * Get the number of parents of this widget.
			 */
			std::size_t numParents() const;

			/**
			 * Brings this window to the top of all child widgets amongst this window's parent.
			 * Calling this function is only possible AFTER this widget has been added to
			 * it's parent. If done before, an exception occurs.
			 */
			void bringToTop();

			/**
			 * Brings the given window to the top of all child widgets.
			 * Throws when this is no child.
			 */
			void bringToTop(weak_ptr widget);

			/**
			 * Removes the given widget from this widget.
			 */
			//void remove(const Widget::ptr& widget);




			/**
			 * Get the upper left position of the window.
			 */
			int2 pos() const;

			/**
			 * Get the size of the window, in pixels on the screen.
			 * This is the entire area of the widget on screen.
			 */
			intsize2 size() const;

			/**
			 * Get the rectangle for this widget.
			 * The rectangle specifies both position and size.
			 */
			math::rectangle<int> rect() const;

			/**
			 * Get the client position of this widget.
			 * The background quad will only cover clientPos() to
			 * clientPos() + clientSize(), not the entire widget's
			 * size. When a subclass reimplements this function, it
			 * should also reimplement drawAdditional() to shade
			 * the new free area.
			 */
			int2 clientPos() const;

			/**
			 * Get the size of the window, that can really be used.
			 * The background quad will only cover clientPos() to
			 * clientPos() + clientSize(), not the entire widget's
			 * size. When a subclass reimplements this function, it
			 * should also reimplement drawAdditional() to shade
			 * the new free area.
			 */
			intsize2 clientSize() const;

			/**
			 * Get the client rectangle of the widget.
			 * This is the total area that may be used by sub-widgets
			 * as well as the area that is covered by the background.
			 */
			math::rectangle<int> clientRect() const;

			/**
			 * Get the minimum size for this widget.
			 * A widget will not be resized to less than this value.
			 * Whenever a layout is attached to this widget, not
			 * the configured minimum size, but the one, calculated
			 * by the layout is returned.
			 */
			intsize2 minimumSize() const;

			/**
			 * Get the minimum client size for this widget.
			 */
			intsize2 minimumClientSize() const;

			/**
			 * This is the configured minimum size of this widget.
			 * The actual minimum size will be overwritten, in case
			 * a layout is attached to this widget.
			 */
			virtual intsize2 configuredMinimumSize() const;

			/**
			 * Set the minimum size for this widget.
			 */
			void setMinimumSize(intsize2 minSize);
			void setMinimumWidth(int width);
			void setMinimumHeight(int height);

			/**
			 * Get the maximum size for this widget.
			 * A widget will not be resized to more than this value.
			 */
			intsize2 maximumSize() const;

			/**
			 * This is the configured maximum size of this widget.
			 * The actual maximum size will be overwritten, in case
			 * a layout is attached to this widget.
			 */
			virtual intsize2 configuredMaximumSize() const;

			/**
			 * Set the maximum size for this widget.
			 */
			void setMaximumSize(intsize2 maxSize);
			void setMaximumWidth(int width);
			void setMaximumHeight(int height);

			/**
			 * Get the extends of the nonclient area.
			 * The returned vector contains the following information: (left, top, right, bottom).
			 * This function is called alot and should not perform heavy computations.
			 */
			virtual int4 padding() const;

			/**
			 * Get the screen position of this widget.
			 * @note   this function recursively travels through all parents to find the current screen position
			 *         and thus has an O(n) access time
			 */
			int2 screenPos() const;

			/**
			 * Set the position of the widget.
			 *
			 * @param pos   the position of this widget, relative to the parent
			 */
			void setPos(const int2& pos);

			/**
			 * Move the widget.
			 * Equal to setPos(pos() + delta).
			 */
			void move(const int2& delta);

			/**
			 * Set the size of the window.
			 *
			 * @param size   the entire size of the widget
			 */
			void resize(intsize2 size);

			/**
			 * Set the client position of the widget relative to the parent.
			 *
			 * @param clientPos   the position of the client area, relative to the parent (excluding any frame)
			 */
			void setClientPos(const int2& clientPos);

			/**
			 * Set the client size of the widget.
			 *
			 * @param clientSize   the size of the client area of the widget (the entire widget's area might be bigger, depending on the frame(s))
			 */
			void setClientSize(const intsize2& clientSize);




			/**
			* Get the resizing policy of this widget.
			*/
			Resizing::Type resizing() const;

			/**
			* Set the resizing policy of this widget.
			*/
			void setResizing(Resizing::Type type);




			/**
			 * Test if the widget is visible or not.
			 * Does not test if the widget is covered by another one.
			 */
			bool visible() const;

			/**
			 * Set the visible flag of this widget.
			 */
			void setVisible(bool visible);



			/**
			 * Tests if the widget is being hovered by the cursor or not.
			 */
			bool mouseHovered() const;

			/**
			 * Tests if the widget is being hovered and if the left mouse button is pressed down.
			 */
			bool mousePressed() const;

			/**
			 * Returns the current state of the widget: clicked, hovered or none.
			 */
			WidgetState::Type state() const;



			//
			// Signal connectors
			//

			boostext::connector<notify>        stateChanged;
			boostext::connector<notify>        mouseEnter;
			boostext::connector<notify>        mouseLeave;
			boostext::connector<notify>        mouseClicked;
			boostext::connector<notify>        mouseDblClicked;
			boostext::connector<notify>        moved;
			boostext::connector<notify>        resized;
			boostext::connector<notify>        destroyed;
			boostext::connector<notify_widget> addWidget;
			boostext::connector<notify_widget> removeWidget;




			/**
			 * Get the number of quads, this widget is composed of.
			 * This number is obviously meaningless, when the state of the
			 * widget is being changed afterwards, but must return the correct
			 * number until such a state-change occurs.
			 */
			uint32 numQuads() const;

			/**
			 * This function does a hit test of the widget's geometry against the mouse cursor.
			 * It subsequently updates the hover & clicked flag and emits the proper signals.
			 * Only those widgets, that can receive a mouse click will be tested (If a parent is
			 * not hovered, neither of its children will be tested).
			 *
			 * @return true when this widget or a child is being hovered
			 */
			bool hitTest(const NestedState& state);

			/**
			 * Updates the widget, based on the current state.
			 * All widgets will be updated, no matter what their current state is.
			 */
			virtual void update(NestedState& state);

			/**
			 * Processes user input (keyboard/mouse/etc...).
			 * A subwidget must overwrite this function if it wishes to
			 * receive such input. Any other widget can ignore it.
			 * It is important to note however, that ONLY the current focused
			 * widget receives input.
			 */
			virtual void processInput(QChar c);

			/**
			 * Creates the quads that are required to render this widget and
			 * appends them to the given list.
			 *
			 * Widgets are allowed to modify themselves, in order to render to a cache first.
			 * This is especially useful for expensive draw calls, like for a text because of
			 * the many glyph lookups.
			 */
			void render(Quads& quads, const NestedState& state);

		protected:

			/**
			 * @returns a weak_ptr for this instance
			 * @throws nothing
			 */
			weak_ptr weak_from_this();

			/**
			 * @returns a shared_ptr for this instance
			 * @throws boost::bad_weak_ptr in case this object is currently being destroyed
			 */
			ptr shared_from_this();

			/**
			 * @returns a shared_ptr for this instance
			 * @throws boost::bad_weak_ptr in case this object is currently being destroyed
			 */
			const_ptr shared_from_this() const;



			/**
			 * Get the type of this widget.
			 */
			virtual Widgets::Type type() const;

			/**
			 * This function is bound to the stateChanged() signal and requests the current background
			 * color and -glyph that is used to render the center glyph of this widget.
			 */
			void onStateChanged(weak_ptr that);

			/**
			 * Sets the style for this particular widget.
			 * Will only affect children if a subclass decides to do so (ie,
			 * the style demands a child has another style).
			 */
			virtual void styleChanged();



			/**
			 * Get access to the background glyph.
			 */
			const Glyph& backgroundGlyph() const;

			/**
			 * Get the current background color of the widget.
			 */
			const math::color& backgroundColor() const;

			/**
			 * Renders any additional geometry that is needed to display this widget.
			 * This function must be reimplemented in a subclass (a widget that renders
			 * nothing is pretty useless).
			 *
			 * Widgets are allowed to modify themselves, in order to render to a cache first.
			 * This is especially useful for expensive draw calls, like for a text because of
			 * the many glyph lookups.
			 */
			virtual void renderAdditional(Quads& quads, const NestedState& state);

			/**
			 * Get the number of additional quads, this widget is composed of.
			 * This function must be reimplemented in a subclass.
			 *
			 * No subclass is allowed to modify a parent or widget within this function call, overwritten or not.
			 * Doing so will lead to undefined behavior.
			 */
			virtual uint32 numAdditionalQuads() const;

		private:

			/**
			 * This function is called whenever a child has been clicked
			 * in order to maintain a sorted list.
			 */
			void onChildClicked(weak_ptr that);



			/**
			 * Set the hover flag.
			 */
			void setHover(bool);

			/**
			 * Obtain the iterator for a particular widget.
			 */
			WidgetList::iterator find(const Widget::ptr& widget);
			WidgetList::const_iterator find(const Widget::ptr& widget) const;

			/**
			 * Creates the quad that represents the background of the widget.
			 * The quad takes up the entire client space and shades it in the specified
			 * background color.
			 *
			 * @param quads       the vector that receives the quad of the background
			 * @param parentPos   the position of the parent in screen space
			 */
			void renderBackground(Quads& quads, const NestedState& state) const;

		private:

			/// This is the entire style document
			BasicStyleDefinition::ptr   m_styleDefinition;

			/// This is the style that is used to render this widget
			WidgetStyle::ptr            m_widgetStyle;

			/// This is the identifier of the style
			WidgetStyleIdentifier       m_styleId;

			/// This is the layout (if present) that resizes & positions all children accordingly
			Layout::ptr                 m_layout;

			/// weak_ptr of this widget
			Widget::weak_ptr            m_this;

			/// The parent of this widget (or null_ptr if none)
			Widget::weak_ptr            m_parent;

			/// Those are the sub-widgets of this widget.
			/// The list also defines the order of widgets, with the first item
			/// being the topmost and the last one being the least.
			WidgetList                  m_widgets;
			WidgetList                  m_tmpWidgets;


			/// This is the current background color of this widget
			math::color                 m_backgroundColor;

			/// This is the current background glyph of this widget
			Glyph                       m_backgroundGlyph;



			/// The position is given relative to the parent widget.
			/// When this is the topmost window, the position is relative
			/// to the upper left corner of the screen.
			/// Both are in pixels.
			math::rectangle<int>        m_rect;

			/// The minimum size for the entire widget (client + nonclient area)
			intsize2                    m_minimumSize;

			/// The maximum size for the entire widget (client + nonclient area)
			intsize2                    m_maximumSize;



			/// Tells if the widget is visible or invisible
			/// The widget will not be rendered, when invisible (obviously).
			bool                        m_visible;

			/// Defines what resizing policy is in effect
			Resizing::Type              m_resizingType;

			/// Defines whether or not the widget is currently resizing
			bool                        m_resizing;


			/// This flag tells if the mouse has hovered over
			/// this widget during the last update, if it was clicked,
			/// or neither of the two
			bool                        m_mouseHovered;
			bool                        m_mousePressed;
			WidgetState::Type           m_widgetState;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////

#pragma warning (push)
